//キャンバス要素のコンテキスト
var ctx = null;
//画像データ
var img_block = null;
var img_coin = null;
var img_mario = null;
var img_goomba = null;
//タイマー(50ミリ毎秒)
var timer = null;

//定数
//キャラクタのサイズ
var CH16 = 16;
//キャラクタのサイズの中点
var CH08 = CH16 / 2;
//画面サイズ
var DS256 = 256;
//重力加速度
var GRAVITY = -2;
//横方向の最高速度(地形判定のために、CH16より小さくすること。さもないと壁を突き抜けてしまう。)
var MAX_SPEED_X = 8;
//縦方向の最高速度(地形判定のために、CH16より小さくすること。さもないと壁を突き抜けてしまう。)
var MAX_SPEED_Y = 15;
//ジャンプ力
var JUMP_POWER = 5;
//ジャンプボタンを押しつづけるとジャンプ量が増す。押した時間で5段階まで変わる。
var JUMP_BOOST = 5;
var GOOMBA_SPEED = 1;
//ステージのデータ。
var Stage = null;
var stage_width = 0;
var stage_height = 0;



//x_prev   前回のx座標  y_prev   前回のy座標
//x        現在のx座標  y        現在のy座標
//x_accel  横の加速度   y_accel  縦の加速度
//on_gnd 地上にいるかのフラグ(地上では、ジャンプが可能、また、摩擦を受ける)
//jump_boost ボタンを押しつづけるとジャンプが高くなる
var Charactor = Class.create({
  initialize: function(x,y,vx,vy) {
    this.x = x;
    this.y = y;
    this.x_prev = x - vx;
    this.y_prev = y - vy;
    this.x_accel = 0;
    this.y_accel = 0;
    this.on_gnd = false;
    this.jump_boost = 0;
  }
});

//ステージの横スクロール量。
var Scroll;

var Mario;
var Goombas;

//コインのエフェクト
var Coin;

function clamp(x,min,max) {
  return Math.max(Math.min(x,max),min);
}

function Goomba_move(goomba) {
  //Verlet法
  var x_temp = goomba.x;
  goomba.x += clamp(goomba.x - goomba.x_prev + goomba.x_accel, -MAX_SPEED_X, MAX_SPEED_X);
  goomba.x_prev = x_temp;

  var y_temp = goomba.y;
  goomba.y += clamp(goomba.y - goomba.y_prev + goomba.y_accel - GRAVITY,  -MAX_SPEED_Y, MAX_SPEED_Y);
  goomba.y_prev = y_temp;

  //画面の上端、下端に行けないようにする。
  goomba.y = clamp(goomba.y,0,DS256);
  //ステージデータの配列境界を越えないためのチェック
  if (goomba.x >= stage_width || goomba.y >= stage_height) return;

  var tx,ty;
  //空中にいるフラグを立てる
  goomba.on_gnd = false;

  if (goomba.y - goomba.y_prev < 0) {
    //上方向に速度があるならば
    //頭で衝突判定する
    tx = Math.floor((goomba.x+CH08) / CH16);
    ty = Math.floor( goomba.y       / CH16);
    //ブロックがあったら、y方向の移動を制限する
    if (Stage[ty][tx] == 1) {
      goomba.y = ty * CH16 + CH16;
    };
  } else {
    //下方向に速度があるならば
    //かかとで衝突判定する
    tx = Math.floor((goomba.x+CH08) / CH16);
    ty = Math.floor((goomba.y+CH16) / CH16);
    //ブロックがあったら、y方向の移動を制限する
    if (Stage[ty][tx] == 1) {
      goomba.y = ty * CH16 - CH16;
      //地上にいるフラグを立てる
      goomba.on_gnd = true;
    };
  };
  if (goomba.x - goomba.x_prev < 0) {
    //左方向に速度があるならば
    //背中で衝突判定する
    tx = Math.floor( goomba.x       / CH16);
    ty = Math.floor((goomba.y+CH08) / CH16);
    //ブロックがあったら、x方向の移動を制限する。反発する。
    if (Stage[ty][tx] == 1) {
      goomba.x = tx * CH16 + CH16 + GOOMBA_SPEED;
    };
  } else {
    //右方向に速度があるならば
    //腹で衝突判定する
    tx = Math.floor((goomba.x+CH16) / CH16);
    ty = Math.floor((goomba.y+CH08) / CH16);
    //ブロックがあったら、x方向の移動を制限する。反発する。
    if (Stage[ty][tx] == 1) {
      goomba.x = tx * CH16 - CH16 - GOOMBA_SPEED;
    };
  };
}

function Mario_move() {
  //摩擦(ただし空中では摩擦なし)
  var friction = 0;
  if (Mario.on_gnd) {
    //進行方向と逆の力を受ける
    if (Mario.x - Mario.x_prev > 0.3) {
      friction = -0.3;
    };
    if (Mario.x - Mario.x_prev < -0.3) {
      friction = +0.3;
    };
  };

  //ジャンプ量増加を打ち切る
  if (--Mario.jump_boost < 0) Mario.y_accel = 0;

  //Verlet法
  var x_temp = Mario.x;
  Mario.x += clamp(Mario.x - Mario.x_prev + Mario.x_accel + friction, -MAX_SPEED_X, MAX_SPEED_X);
  Mario.x_prev = x_temp;

  var y_temp = Mario.y;
  Mario.y += clamp(Mario.y - Mario.y_prev + Mario.y_accel - GRAVITY,  -MAX_SPEED_Y, MAX_SPEED_Y);
  Mario.y_prev = y_temp;

  //ステージの横スクロールより左に行けないようにする
  Mario.x = Math.max(Mario.x,Scroll);

  //画面の上端、下端に行けないようにする。
  Mario.y = clamp(Mario.y,0,DS256);

  //ステージデータの配列境界を越えないためのチェック
  if (Mario.x >= stage_width || Mario.y >= stage_height) return;

  var tx,ty;

  //空中にいるフラグを立てる
  Mario.on_gnd = false;

  if (Mario.y - Mario.y_prev < 0) {
    //上方向に速度があるならば
    //頭で衝突判定する
    tx = Math.floor((Mario.x+CH08) / CH16);
    ty = Math.floor( Mario.y       / CH16);
    //ブロックがあったら、y方向の移動を制限する
    if (Stage[ty][tx] == 1) {
      Mario.y = ty * CH16 + CH16;
      //ブロックを壊す
      Stage[ty][tx] = 0;
      Coin = [tx,ty,0];
    };
  } else {
    //下方向に速度があるならば
    //かかとで衝突判定する
    tx = Math.floor((Mario.x+CH08) / CH16);
    ty = Math.floor((Mario.y+CH16) / CH16);
    //ブロックがあったら、y方向の移動を制限する
    if (Stage[ty][tx] == 1) {
      Mario.y = ty * CH16 - CH16;
      //地上にいるフラグを立てる
      Mario.on_gnd = true;
      //ジャンプブーストを回復する
      Mario.jump_boost = JUMP_BOOST;
    };
  };
  if (Mario.x - Mario.x_prev < 0) {
    //左方向に速度があるならば
    //背中で衝突判定する
    tx = Math.floor( Mario.x       / CH16);
    ty = Math.floor((Mario.y+CH08) / CH16);
    //ブロックがあったら、x方向の移動を制限する
    if (Stage[ty][tx] == 1) {
      Mario.x = tx * CH16 + CH16;
    };
  } else {
    //右方向に速度があるならば
    //腹で衝突判定する
    tx = Math.floor((Mario.x+CH16) / CH16);
    ty = Math.floor((Mario.y+CH08) / CH16);
    //ブロックがあったら、x方向の移動を制限する
    if (Stage[ty][tx] == 1) {
      Mario.x = tx * CH16 - CH16;
    };
  };

  var correction = 0;
  for (var i = 0; i - correction < Goombas.length; i++) {
    var goomba = Goombas[i - correction];
    if (Mario.x + CH08 >= goomba.x &&
	Mario.x <= goomba.x + CH16) {
      if (Mario.y - Mario.y_prev > 0) {
	if (Mario.y + CH16 + 8 > goomba.y &&
	    Mario.y < goomba.y) {
	  Mario.y_prev = Mario.y + Mario.y - Mario.y_prev;
	  Goombas.splice(i - correction, 1);
	  correction++;
	};
      } else {
	if (Mario.y >= goomba.y &&
	    Mario.y < goomba.y + CH16) {
	  goomba.y -= 4;
	};
      };
    };
  };
}

function main() {
  //画面クリア
  ctx.clearRect(0,0,DS256,DS256)
  //ステージデータをもとにブロックを描画
  for (var j = 0; j < Stage.length; j++) {
    for (var i = 0; i < Stage[0].length; i++) {
      //表示範囲にあるブロックのみ選ぶ。
      //画面からちょっとはみ出たブロックも描画するため、CH16分だけ範囲を大きくとる。
      if (i * CH16 >= Scroll - CH16 && i * CH16 <= Scroll + DS256 + CH16) {
	if (Stage[j][i] == 1) {
	  ctx.drawImage(img_block,
			i * CH16 - Scroll,
			j * CH16,
			CH16,
			CH16);
	};
      };
    };
  };

  //コインのエフェクトを描画
  if (Coin) {
    ctx.drawImage(img_coin,
		  Coin[0] * CH16 - Scroll,
		  Coin[1] * CH16 - Coin[2],
		  CH16,
		  CH16);
    if (++Coin[2] > 8) Coin = null;
  }

  Mario_move();
  //Marioを描画
  ctx.drawImage(img_mario,
		Mario.x - Scroll,
		Mario.y,
		CH16,
		CH16);


  for (var i = 0; i < Goombas.length; i++) {
    var goomba = Goombas[i];
    Goomba_move(goomba);
    //Goombaを描画
    ctx.drawImage(img_goomba,
		  goomba.x - Scroll,
		  goomba.y,
		  CH16,
		  CH16);
  }


  //プレイヤーの移動に応じてステージを横スクロールする
  if (Scroll < Mario.x - 64) {
    Scroll = Mario.x - 64;
  };

  //ステージデータの範囲を越えるところでゲームクリア
  if (Mario.x >= stage_width){
    $('msg').innerHTML = '＼(＾o＾)／';
    setTimeout(init,1000);
    return;
  };
  //ステージデータの範囲を越えるところで死亡
  if (Mario.y >= stage_height){
    $('msg').innerHTML = '／(^o^)＼';
    setTimeout(init,1000);
    return;
  };


}

function onkeydown(event) {
  switch(event.keyCode) {
    case Event.KEY_LEFT:
      Mario.x_accel = -0.7;
      break;
    case Event.KEY_RIGHT:
      Mario.x_accel = +0.7;
      break;
    case Event.KEY_UP:
      //地上にいるときだけジャンプできる
      if (Mario.on_gnd) Mario.y_accel = -JUMP_POWER;
      break;
    case Event.KEY_DOWN:
      break;
  };
}

function onkeyup(event) {
  switch(event.keyCode) {
    case Event.KEY_LEFT:
      Mario.x_accel = 0;
      break;
    case Event.KEY_RIGHT:
      Mario.x_accel = 0;
      break;
    case Event.KEY_UP:
      Mario.y_accel = 0;
      break;
    case Event.KEY_DOWN:
      break;
  };
}

function init() {
  var canvas = document.getElementById("canvas");
  if (!canvas.getContext) return;
  ctx = canvas.getContext('2d');
  img_block     = new Image();
  img_block.src = 'block.png';
  img_coin      = new Image();
  img_coin.src  = 'coin.png';
  img_mario     = new Image();
  img_mario.src = 'mario.png';
  img_goomba     = new Image();
  img_goomba.src = 'goomba.png';
  Event.observe(document, 'keydown', onkeydown);
  Event.observe(document, 'keyup'  , onkeyup);
  $('msg').innerHTML = '';
  if (timer) timer.stop();
  timer = new PeriodicalExecuter(main,0.05);
  Scroll = 0;
  Coin = null;
  Mario = new Charactor(32,108,0,0);
  Goombas = [
    new Charactor(286,226,-GOOMBA_SPEED,0),
    new Charactor(266,226,-GOOMBA_SPEED,0),
    new Charactor(226,226,-GOOMBA_SPEED,0)
  ];
  Stage =   [
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0],
    [0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0],
    [0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0],
    [0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,1,0,1,1,0,0],
    [0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
  ];
  stage_width  = (Stage[0].length - 1) * CH16;
  stage_height = (Stage   .length - 1) * CH16;
}

window.onload = init;

